using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Reflection;
using Needle.Core.Injection;
using Needle.Utils;

namespace Needle.Core.Configuration {
    /// <summary>
    /// Describes an injected constructor.
    /// </summary>
    [Description("Describes an injected constructor.")]
    public class ConstructorElement : ConfigurationElement {
        /// <summary>
        /// the list of injected arguments.
        /// </summary>
        [ConfigurationCollection(typeof(ArgumentElement))]
        [ConfigurationProperty(Element.Args, IsRequired = false, IsDefaultCollection = true)]
        [Description("The list of injected arguments.")]
        public ArgumentElementCollection Args
        {
            get { return (ArgumentElementCollection)this[Element.Args]; }
        }

        internal Needle.Core.Injection.IInjectionSite GetInjectionSite(Type constructedType)
        {
            List<Type> parameterTypes = new List<Type>();
            List<InstanceId> depIds = new List<InstanceId>();

            foreach (ArgumentElement arg in Args)
            {
                try
                {
                    Type paramType = Types.GetType(arg.Type);
                    parameterTypes.Add(paramType);
                    if (!String.IsNullOrEmpty(arg.Inject))
                        depIds.Add(InstanceId.Parse(arg.Inject));
                    else
                        depIds.Add(InstanceId.Null);
                }
                catch (Exception ex)
                {
                    throw new ConfigurationErrorsException("Error in constructor parameter configuration.", ex, arg.ElementInformation.Source, arg.ElementInformation.LineNumber);
                }
            }

            ConstructorInfo ctor = constructedType.GetConstructor(parameterTypes.ToArray());
            if (ctor==null)
            {
                throw new ConfigurationErrorsException("No constructor found for parameter list.", this.ElementInformation.Source, this.ElementInformation.LineNumber);
            }

            try
            {
                ParameterInfo[] parInfo = ctor.GetParameters();
                List<IParameter> parameters = new List<IParameter>();
                for (int i = 0; i < parInfo.Length; i++)
                {
                    InstanceId depId = depIds[i];
                    Parameter p;
                    if (depId.IsNull)
                        p = new Parameter(parInfo[i], (string)null);
                    else
                        p = new Parameter(parInfo[i], depId);
                    parameters.Add(p);
                }

                InjectionSite ctorSite = new InjectionSite(ctor, -1, parameters);
                return ctorSite;
            }
            catch (Exception ex)
            {
                throw new ConfigurationErrorsException("Error in constructor parameter configuration.", ex, this.ElementInformation.Source, this.ElementInformation.LineNumber);
            }
        }
    }

}
